package com.yc.sole.system.biz;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.google.common.collect.Lists;
import com.yc.sole.common.constant.CacheConstant;
import com.yc.sole.common.constant.PubConstant;
import com.yc.sole.common.enums.UserType;
import com.yc.sole.framework.context.SecureContextHolder;
import com.yc.sole.framework.exception.BaseException;
import com.yc.sole.framework.mybatis.page.Paging;
import com.yc.sole.framework.mybatis.util.PageUtil;
import com.yc.sole.framework.redis.RedissonService;
import com.yc.sole.system.dto.*;
import com.yc.sole.system.entity.UserRole;
import com.yc.sole.system.exception.SysResultEnum;
import com.yc.sole.system.repository.*;
import com.yc.sole.system.request.UserQueryReq;
import com.yc.sole.system.request.UserReq;
import com.yc.sole.system.request.UserUpdatePwdReq;
import com.yc.sole.system.response.UserResp;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 服务接口实现
 *
 * @author yizuomin
 * @date 2023-03-26
 **/
@Slf4j
@Service
@RequiredArgsConstructor
public class UserBiz {

    private final UserRepository userRepository;
    private final UserRoleRepository userRoleRepository;
    private final DeptRepository deptRepository;
    private final RedissonService redissonService;
    private final RoleRepository roleRepository;
    private final ConfigRepository configRepository;

    /**
     * 新增
     *
     * @param userReq
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveUser(UserReq userReq) {
        // 验证用户名是否重复
        validateUsername(userReq.getUsername());
        // 验证手机是否重复
        validatePhone(userReq.getPhoneNum());
        ConfigDto configDto = configRepository.selectConfig();
        if (Objects.isNull(configDto)) {
            throw new BaseException(SysResultEnum.CONFIG_NOT_INIT);
        }
        UserDto dto = new UserDto();
        BeanUtil.copyProperties(userReq, dto);
        dto.setUserType(UserType.USER);
        //dto.setPassword(BCrypt.hashpw(userReq.getPassword()));
        dto.setPassword(BCrypt.hashpw(configDto.getDefaultPassword()));
        Long userId = userRepository.saveUser(dto);
        //新增用户角色关联
        if (CollUtil.isNotEmpty(userReq.getRoleIds())) {
            List<UserRoleDto> userRoleList = userReq.getRoleIds().stream().map(roleId -> {
                UserRoleDto userRoleDto = new UserRoleDto();
                userRoleDto.setRoleId(roleId);
                userRoleDto.setUserId(userId);
                return userRoleDto;
            }).toList();
            userRoleRepository.saveBatchUserRole(userRoleList);
        }
    }

    /**
     * 修改
     *
     * @param userReq
     */
    public void updateUserById(UserReq userReq) {
        UserDto dto = new UserDto();
        dto.setId(userReq.getId());
        dto.setEmail(userReq.getEmail());
        dto.setState(userReq.getState());
        dto.setDeptId(userReq.getDeptId());
        dto.setRoleIds(userReq.getRoleIds());
        userRepository.updateUserById(dto);
        //删除用户角色关联
        userRoleRepository.deleteUserRoleByUserIds(Lists.newArrayList(userReq.getId()));
        //新增用户角色关联
        if (CollUtil.isNotEmpty(userReq.getRoleIds())) {
            List<UserRoleDto> userRoleList = userReq.getRoleIds().stream().map(roleId -> {
                UserRoleDto userRoleDto = new UserRoleDto();
                userRoleDto.setRoleId(roleId);
                userRoleDto.setUserId(userReq.getId());
                return userRoleDto;
            }).toList();
            userRoleRepository.saveBatchUserRole(userRoleList);
        }
    }

    /**
     * 删除
     *
     * @param ids
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteUser(Collection<Long> ids) {
        // 删除用户
        userRepository.deleteUserByIds(ids);
        //删除用户角色关联
        userRoleRepository.deleteUserRoleByUserIds(ids);
    }

    /**
     * 分页查询
     *
     * @param page
     * @param userQueryReq
     * @return
     */
    public Paging<UserResp> selectUserPage(Paging<UserQueryReq> page, UserQueryReq userQueryReq) {
        Paging<UserDto> paging = userRepository.selectUserPage(page, userQueryReq);
        Paging<UserResp> userRespPaging = PageUtil.pageCopy(paging, UserResp.class);
        if (CollUtil.isEmpty(userRespPaging.getRecords())) {
            return userRespPaging;
        }
        Set<Long> deptIds = userRespPaging.getRecords().stream().map(UserResp::getDeptId).collect(Collectors.toSet());
        Set<Long> userIds = userRespPaging.getRecords().stream().map(UserResp::getId).collect(Collectors.toSet());
        Map<Long, List<UserRoleDto>> userRoleMap = userRoleRepository.selectRoleIdsByUserIds(userIds).stream().collect(Collectors.groupingBy(UserRole::getUserId));
        Map<Long, DeptDto> longSysDeptDtoMap = deptRepository.selectDeptByIds(deptIds).stream().collect(Collectors.toMap(DeptDto::getId, v -> v));
        for (UserResp userResp : userRespPaging.getRecords()) {
            List<UserRoleDto> userRoleDtos = userRoleMap.get(userResp.getId());
            Set<Long> roleIds = userRoleDtos.stream().map(UserRoleDto::getRoleId).collect(Collectors.toSet());
            userResp.setRoleIds(roleIds);
            List<String> roleNameList = roleRepository.selectRoleByIds(roleIds).stream().map(RoleDto::getRoleName).collect(Collectors.toList());
            userResp.setRoleNames(CharSequenceUtil.join(",", roleNameList));
            DeptDto deptDto = longSysDeptDtoMap.get(userResp.getDeptId());
            if (Objects.nonNull(deptDto)) {
                userResp.setDeptName(deptDto.getDeptName());
            }
        }
        return userRespPaging;
    }

    /**
     * 修改用户信息
     *
     * @param updatePwdReq
     */
    @Transactional(rollbackFor = Exception.class)
    public void updatePassword(UserUpdatePwdReq updatePwdReq) {
        UserDto userDto = userRepository.selectUserById(SecureContextHolder.getUserId());
        // 验证原密码
        if (!BCrypt.checkpw(updatePwdReq.getPassword(), userDto.getPassword())) {
            throw new BaseException(SysResultEnum.AUTH_PASSWORD_ERROR);
        }
        // 验证新密码和确认密码
        if (!updatePwdReq.getNewPassword().equals(updatePwdReq.getConfirmNewPassword())) {
            throw new BaseException(SysResultEnum.AUTH_CONFIRM_PASSWORD_ERROR);
        }
        UserDto updateDto = new UserDto();
        updateDto.setId(userDto.getId());
        updateDto.setPassword(BCrypt.hashpw(updatePwdReq.getNewPassword()));
        userRepository.updateUserById(updateDto);
    }

    /**
     * 重制用户密码
     *
     * @param userId
     */
    @Transactional(rollbackFor = Exception.class)
    public void resetPassword(Long userId) {
        UserDto userDto = userRepository.selectUserById(userId);
        if (Objects.isNull(userDto)) {
            throw new BaseException(SysResultEnum.USER_NOT_FOUND);
        }
        ConfigDto configDto = configRepository.selectConfig();
        if (Objects.isNull(configDto)) {
            throw new BaseException(SysResultEnum.CONFIG_NOT_INIT);
        }
        String password;
        if (CharSequenceUtil.isNotBlank(configDto.getDefaultPassword())) {
            password = BCrypt.hashpw(configDto.getDefaultPassword());
        } else {
            password = BCrypt.hashpw(PubConstant.DEFAULT_PASSWORD);
        }
        UserDto updateDto = new UserDto();
        updateDto.setId(userId);
        updateDto.setPassword(password);
        userRepository.updateUserById(updateDto);
    }

    /**
     * 验证用户名是否重复
     *
     * @param username
     */
    private void validateUsername(String username) {
        redissonService.tryLock(CacheConstant.LOCK_PREFIX + "USERNAME:" + username, 1, 3, () -> {
            boolean existByUsername = userRepository.existByUsernameOrPhone(username);
            if (existByUsername) {
                throw new BaseException(SysResultEnum.USER_NAME_IS_REGISTER);
            }
        });
    }

    /**
     * 验证手机是否重复
     *
     * @param phone
     */
    private void validatePhone(String phone) {
        redissonService.tryLock(CacheConstant.LOCK_PREFIX + "PHONE:" + phone, 1, 3, () -> {
            boolean existByPhone = userRepository.existByUsernameOrPhone(phone);
            if (existByPhone) {
                throw new BaseException(SysResultEnum.USER_PHONE_IS_REGISTER);
            }
        });
    }

    /**
     * 根据用户id查询
     *
     * @param userId
     * @return
     */
    public UserResp selectUserById(Long userId) {
        UserResp userResp = BeanUtil.copyProperties(userRepository.selectUserById(userId), UserResp.class);
        if (Objects.isNull(userResp)) {
            throw new BaseException(SysResultEnum.USER_NOT_FOUND);
        }
        List<Long> roleIds = userRoleRepository.selectRoleIdsByUserId(userId);
        userResp.setRoleIds(roleIds);

        List<String> roleNameList = roleRepository.selectRoleByIds(roleIds).stream().map(RoleDto::getRoleName).toList();
        userResp.setRoleNames(CharSequenceUtil.join(",", roleNameList));

        DeptDto deptDto = deptRepository.selectDeptById(userResp.getDeptId());
        if (Objects.nonNull(deptDto)) {
            userResp.setDeptName(deptDto.getDeptName());
        }
        return userResp;
    }
}
